#include <linux/module.h>
#include <asm/desc.h>

#include "exceptions.h"
#include "paging.h"

#define info(...) printk("<1> info " __VA_ARGS__)

#define PFEC(x) ( ((x) & 1) + \
                  (((x)>>1) & 1)*10 + \
                  (((x)>>2) & 1)*100 + \
                  (((x)>>3) & 1)*1000 + \
                  (((x)>>4) & 1)*10000 )
                  
#pragma GCC push_options
#pragma GCC optimize ("O0")

void int_info_header(void)
{
  info("%-10s%6s%9s%11s%11s",
       "test",
       "int?",
       "int num",
       "err code?",
       "err_code"
      );
}

void int_info(char *name)
{
  info("%-10s%6s%9hu%11s%11d",
       name,
       (last_int_info.interrupt ? "yes" : "no"),
       last_int_info.int_num,
       (last_int_info.has_error_code ? "yes" : "no"),
       PFEC(last_int_info.unused2)
      );
}

struct paging_vertex vertices[5];
struct paging_entry entries[4];
struct paging pg;

/*
 * init simple paging
 * 
 * 0 -> 1 -> 2 -> 3 -> [4]
 */

static void my_init_paging(void)
{
  int i;
  pg.paging_mode = PM_64;
  pg.root = vertices;
  pg.paging_struct_list = vertices;
  
  for(i=0; i<4; ++i) {
    vertices[i].next = &vertices[i+1];
    vertices[i].entries = &entries[i];
    vertices[i].n_entries = 1;
    
    vertices[i].entries[0].pos = 1;
    vertices[i].entries[0].child = &vertices[i+1];
    vertices[i].entries[0].flags = PF_PRESENT | PF_WRITE | PF_UNEXECUTABLE;
  }

  vertices[3].next = 0;
  
  vertices[4].next = 0;
  vertices[4].entries = 0;
  vertices[4].n_entries = 0;
}



static int my_init(void)
{int *pp;
    int x;
    int *virt;
    info("begin");

    init_exception_handling();
    
    int_info_header();
    
    my_init_paging();
    
    init_paging(&pg);
    
    
    
    virt = (int *) ((u64)vertices[0].entries[0].pos << 39 |
                    (u64)vertices[1].entries[0].pos << 30 |
                    (u64)vertices[2].entries[0].pos << 21 |
                    (u64)vertices[3].entries[0].pos << 12 |
                    17);

    /* try read */
    EXCEPTIONS_BEGIN("test1");
    x = *virt;
    EXCEPTIONS_END("test1");
    int_info("test1");
    printk("\ninfo test_case %s %llx", "test1", (u64)virt);
    
    EXCEPTIONS_BEGIN("test2");
    asm("movq %1, %%rax\n\t"
        "jmp *%%rax" :"=r"(pp): "r"(virt));
    EXCEPTIONS_END("test2");
    int_info("test2");
    
    EXCEPTIONS_BEGIN("test11");
    *virt = x;
    EXCEPTIONS_END("test11");
    int_info("test11");
    
    
    
    virt = (int *) ((u64)vertices[0].entries[0].pos << 39 |
                    ((u64)vertices[1].entries[0].pos+2) << 30 |
                    (u64)vertices[2].entries[0].pos << 21 |
                    (u64)vertices[3].entries[0].pos << 12 |
                    17);

    /* try read */
    EXCEPTIONS_BEGIN("test3");
    x = *virt;
    EXCEPTIONS_END("test3");
    int_info("test3");
    
    /*try execute*/
    
    EXCEPTIONS_BEGIN("test4");
    asm("myjmp: jmp 1");//%1" :"=r"(pp): "r"(virt));
    EXCEPTIONS_END("test4");
    int_info("test4");
    
    info("finish");
    
    return 0;
}

static void my_exit(void) {
    info("exit");
}

#pragma GCC pop_options

module_init(my_init);
module_exit(my_exit);
